package com.gitee.wsl.collections.map

import com.gitee.wsl.base.Objects.equals
import com.gitee.wsl.collections.iterator.EmptyMutableIterator
import com.gitee.wsl.collections.iterator.api.MutableMapEntry
import com.gitee.wsl.collections.iterator.api.MutableMapIterator
import com.gitee.wsl.collections.iterator.api.ResettableIterator
import com.gitee.wsl.ext.base.COMMA
import kotlin.concurrent.Volatile
import kotlin.jvm.Transient

/**
 * A `Map` implementation that stores data in simple fields until
 * the size is greater than 3.
 *
 *
 * This map is designed for performance and can outstrip HashMap.
 * It also has good garbage collection characteristics.
 *
 *
 *  * Optimised for operation at size 3 or less.
 *  * Still works well once size 3 exceeded.
 *  * Gets at size 3 or less are about 0-10% faster than HashMap,
 *  * Puts at size 3 or less are over 4 times faster than HashMap.
 *  * Performance 5% slower than HashMap once size 3 exceeded once.
 *
 *
 *
 * The design uses two distinct modes of operation - flat and delegate.
 * While the map is size 3 or less, operations map straight onto fields using
 * switch statements. Once size 4 is reached, the map switches to delegate mode
 * and only switches back when cleared. In delegate mode, all operations are
 * forwarded straight to a HashMap resulting in the 5% performance loss.
 *
 *
 *
 * The performance gains on puts are due to not needing to create a Map Entry
 * object. This is a large saving not only in performance but in garbage collection.
 *
 *
 *
 * Whilst in flat mode this map is also easy for the garbage collector to dispatch.
 * This is because it contains no complex objects or arrays which slow the progress.
 *
 *
 *
 * Do not use `Flat3Map` if the size is likely to grow beyond 3.
 *
 *
 *
 * **Note that Flat3Map is not synchronized and is not thread-safe.**
 * If you wish to use this map from multiple threads concurrently, you must use
 * appropriate synchronization. The simplest approach is to wrap this map
 * using [java.util.Collections.synchronizedMap]. This class may throw
 * exceptions when accessed by concurrent threads without synchronization.
 *
 *
 * @param <K> the type of the keys in this map
 * @param <V> the type of the values in this map
 * @since 3.0
</V></K> */
class Flat3Map<K, V> : AbstractMutableMap<K, V> {

    internal abstract class EntryIterator<K, V>(
        private val parent: Flat3Map<K, V>,
    ) {
        private var nextIndex = 0
        private var currentEntry: FlatMapEntry<K, V>? = null

        fun hasNext(): Boolean {
            return nextIndex < parent.size
        }

        fun nextEntry(): MutableMapEntry<K, V> {
            if (!hasNext()) {
                throw NoSuchElementException(NO_NEXT_ENTRY)
            }
            currentEntry = FlatMapEntry(parent, ++nextIndex)
            return currentEntry!!
        }

        fun remove() {
            if (currentEntry == null) {
                throw IllegalStateException(REMOVE_INVALID)
            }
            parent.remove(currentEntry!!.key)
            currentEntry!!.setRemoved(true)
            nextIndex--
            currentEntry = null
        }
    }

    /**
     * EntrySet
     */
    internal class EntrySet<K, V>(
        private val parent: Flat3Map<K, V>,
    ) : AbstractMutableSet<MutableMapEntry<K, V>>() {
         override fun clear() {
            parent.clear()
        }

         override fun iterator(): MutableIterator<MutableMapEntry<K, V>> {
            if (parent.delegateMap != null) {
                return parent.delegateMap!!.entries.iterator()
            }
            if (parent.isEmpty()) {
                return EmptyMutableIterator
            }
            return EntrySetIterator(parent)
        }

        override fun add(element: MutableMapEntry<K, V>): Boolean {
            TODO("Not yet implemented")
        }

        override fun remove(element: MutableMapEntry<K, V>): Boolean {
            val entry = element
            val key = entry.key
            val result = parent.containsKey(key)
            parent.remove(key)
            return result
        }

        override val size: Int
            get() = parent.size


    }

    /**
     * EntrySetIterator and MapEntry
     */
    internal class EntrySetIterator<K, V>(
        parent: Flat3Map<K, V>,
    ) : EntryIterator<K,V>(parent), MutableIterator<MutableMapEntry<K, V>> {
        override fun next(): MutableMapEntry<K, V> {
            return nextEntry()
        }
    }

    internal class FlatMapEntry<K, V>(
        private val parent: Flat3Map<K, V>,
        private val index: Int,
    ) : MutableMapEntry<K, V> {
        @Volatile
        private var removed = false

        override fun equals(other: Any?): Boolean {
            if (removed) {
                return false
            }
            if (other !is Map.Entry<*, *>) {
                return false
            }
            val other = other as MutableMapEntry<K, V>
            return equals(this.key, other.key) &&
                    equals(this.value, other.value)
        }

        override val key: K
            get() {
                if (removed) {
                    throw IllegalStateException(GETKEY_INVALID)
                }
                when (index) {
                    3 -> return parent.key3!!
                    2 -> return parent.key2!!
                    1 -> return parent.key1!!
                }
                throw IllegalStateException("Invalid map index: $index")
            }

        override val value: V
            get() {
                if (removed) {
                    throw IllegalStateException(GETVALUE_INVALID)
                }
                when (index) {
                    3 -> return parent.value3!!
                    2 -> return parent.value2!!
                    1 -> return parent.value1!!
                }
                throw IllegalStateException("Invalid map index: $index")
            }

        override fun hashCode(): Int {
            if (removed) {
                return 0
            }
            val key: Any? = this.key
            val value: Any? = this.value
            return (key?.hashCode() ?: 0) xor (value?.hashCode() ?: 0)
        }

        /**
         * Used by the iterator that created this entry to indicate that
         * [java.util.Iterator.remove] has been called.
         *
         *
         * As a consequence, all subsequent call to [.getKey],
         * [.setValue] and [.getValue] will fail.
         *
         * @param removed the new value of the removed flag
         */
        fun setRemoved(removed: Boolean) {
            this.removed = removed
        }

         override fun setValue(newValue: V): V {
            if (removed) {
                throw IllegalStateException(SETVALUE_INVALID)
            }
            val old = this.value
            when (index) {
                3 -> parent.value3 = newValue
                2 -> parent.value2 = newValue
                1 -> parent.value1 = newValue
                else -> throw IllegalStateException("Invalid map index: $index")
            }
            return old
        }

        override fun toString(): String {
            if (!removed) {
                return this.key.toString() + "=" + this.value
            }
            return ""
        }
    }

    /**
     * FlatMapIterator
     */
    internal class FlatMapIterator<K, V>(
        private val parent: Flat3Map<K, V>,
    ) : MutableMapIterator<K, V>, ResettableIterator<K> {
        private var nextIndex = 0
        private var canRemove = false

        override fun getKey(): K {
            if (!canRemove) {
                throw IllegalStateException(GETKEY_INVALID)
            }
            when (nextIndex) {
                3 -> return parent.key3!!
                2 -> return parent.key2!!
                1 -> return parent.key1!!
            }
            throw IllegalStateException("Invalid map index: $nextIndex")
        }

        override fun getValue(): V {
            if (!canRemove) {
                throw IllegalStateException(GETVALUE_INVALID)
            }
            when (nextIndex) {
                3 -> return parent.value3!!
                2 -> return parent.value2!!
                1 -> return parent.value1!!
            }
            throw IllegalStateException("Invalid map index: $nextIndex")
        }

        override fun hasNext(): Boolean {
            return nextIndex < parent.size
        }

        override fun next(): K {
            if (!hasNext()) {
                throw NoSuchElementException(NO_NEXT_ENTRY)
            }
            canRemove = true
            nextIndex++
            return getKey()
        }

        override fun remove() {
            if (!canRemove) {
                throw IllegalStateException(REMOVE_INVALID)
            }
            parent.remove(getKey())
            nextIndex--
            canRemove = false
        }

        override fun reset() {
            nextIndex = 0
            canRemove = false
        }

        override fun setValue(value: V): V {
            if (!canRemove) {
                throw IllegalStateException(SETVALUE_INVALID)
            }
            val old = getValue()
            when (nextIndex) {
                3 -> parent.value3 = value
                2 -> parent.value2 = value
                1 -> parent.value1 = value
                else -> throw IllegalStateException("Invalid map index: $nextIndex")
            }
            return old
        }

        override fun toString(): String {
            if (canRemove) {
                return "Iterator[" + getKey() + "=" + getValue() + "]"
            }
            return "Iterator[]"
        }
    }

    /**
     * KeySet
     */
    internal class KeySet<K,V>(
        private val parent: Flat3Map<K, V>,
    ) : AbstractMutableSet<K>() {
         override fun clear() {
            parent.clear()
        }

         override fun contains(element: K): Boolean {
            return parent.containsKey(element)
        }

         override fun iterator(): MutableIterator<K> {
            if (parent.delegateMap != null) {
                return parent.delegateMap!!.keys.iterator()
            }
            if (parent.isEmpty()) {
                return EmptyMutableIterator
            }
            return KeySetIterator(parent)
        }

        override fun add(element: K): Boolean {
            TODO("Not yet implemented")
        }

         override fun remove(element: K): Boolean {
            val result = parent.containsKey(element)
            parent.remove(element)
            return result
        }

        override val size: Int
            get() = parent.size


    }

    /**
     * KeySetIterator
     */
    internal class KeySetIterator<K,V>(
        parent: Flat3Map<K, V>,
    ) : EntryIterator<K, V>(parent), MutableIterator<K> {
         override fun next(): K {
            return nextEntry().key
        }
    }

    /**
     * Values
     */
    internal class Values<K,V>(
        private val parent: Flat3Map<K, V>,
    ) : AbstractMutableCollection<V>() {
         override fun clear() {
            parent.clear()
        }

         override fun contains(element: V): Boolean {
            return parent.containsValue(element)
        }

         override fun iterator(): MutableIterator<V> {
            if (parent.delegateMap != null) {
                return parent.delegateMap!!.values.iterator()
            }
            if (parent.isEmpty()) {
                return EmptyMutableIterator
            }
            return ValuesIterator(parent)
        }

        override fun add(element: V): Boolean {
            TODO("Not yet implemented")
        }

        override val size: Int
            get() = parent.size

    }

    /**
     * ValuesIterator
     */
    internal class ValuesIterator<K,V>(
        parent: Flat3Map<K, V>
    ) : EntryIterator<K, V>(parent), MutableIterator<V> {

         override fun next(): V {
            return nextEntry().value
        }

    }

    /** The size of the map, used while in flat mode  */
    @Transient
    override var size : Int = 0
        get() {
           delegateMap?.let { return it.size }
           return field
        }


    /** Hash, used while in flat mode  */
    @Transient
    private var hash1 = 0

    /** Hash, used while in flat mode  */
    @Transient
    private var hash2 = 0

    /** Hash, used while in flat mode  */
    @Transient
    private var hash3 = 0

    /** Key, used while in flat mode  */
    @Transient
    private var key1: K? = null

    /** Key, used while in flat mode  */
    @Transient
    private var key2: K? = null

    /** Key, used while in flat mode  */
    @Transient
    private var key3: K? = null

    /** Value, used while in flat mode  */
    @Transient
    private var value1: V? = null

    /** Value, used while in flat mode  */
    @Transient
    private var value2: V? = null

    /** Value, used while in flat mode  */
    @Transient
    private var value3: V? = null

    /** Map, used while in delegate mode  */
    @Transient
    private var delegateMap: MutableMap<K,V>? = null

    /**
     * Constructs a new instance.
     */
    constructor()

    /**
     * Constructor copying elements from another map.
     *
     * @param map  the map to copy
     * @throws NullPointerException if the map is null
     */
    constructor(map: Map< K,  V>) {
        putAll(map)
    }

    /**
     * Clears the map, resetting the size to zero and nullifying references
     * to avoid garbage collection issues.
     */
     override fun clear() {
        if (delegateMap != null) {
            delegateMap!!.clear() // should aid gc
            delegateMap = null // switch back to flat mode
        } else {
            size = 0
            hash3 = 0
            hash2 = hash3
            hash1 = hash2
            key3 = null
            key2 = key3
            key1 = key2
            value3 = null
            value2 = value3
            value1 = value2
        }
    }

    /**
     * Clones the map without cloning the keys or values.
     *
     * @return a shallow clone
     * @since 3.1
     */
//      fun clone(): Flat3Map<K,V> {
//        try {
//            val cloned = super.clone() as Flat3Map<K,V>
//            if (cloned.delegateMap != null) {
//                cloned.delegateMap = cloned.delegateMap.clone()
//            }
//            return cloned
//        } catch (ex: java.lang.CloneNotSupportedException) {
//            throw java.lang.UnsupportedOperationException(ex)
//        }
//    }

    /**
     * Checks whether the map contains the specified key.
     *
     * @param key  the key to search for
     * @return true if the map contains the key
     */
     override fun containsKey(key: K): Boolean {
        delegateMap?.let { return it.containsKey(key) }
        if (key == null) {
            when (size) {
                3 -> {
                    if (key3 == null) {
                        return true
                    }
                    if (key2 == null) {
                        return true
                    }
                    if (key1 == null) {
                        return true
                    }
                }

                2 -> {
                    if (key2 == null) {
                        return true
                    }
                    if (key1 == null) {
                        return true
                    }
                }

                1 -> if (key1 == null) {
                    return true
                }
            }
        } else if (isNotEmpty()) {
            val hashCode = key.hashCode()
            when (size) {
                3 -> {
                    if (hash3 == hashCode && key == key3) {
                        return true
                    }
                    if (hash2 == hashCode && key == key2) {
                        return true
                    }
                    if (hash1 == hashCode && key == key1) {
                        return true
                    }
                }

                2 -> {
                    if (hash2 == hashCode && key == key2) {
                        return true
                    }
                    if (hash1 == hashCode && key == key1) {
                        return true
                    }
                }

                1 -> if (hash1 == hashCode && key == key1) {
                    return true
                }
            }
        }
        return false
    }

    /**
     * Checks whether the map contains the specified value.
     *
     * @param value  the value to search for
     * @return true if the map contains the key
     */
     override fun containsValue(value: V): Boolean {
        delegateMap?.let { return it.containsValue(value) }
        if (value == null) {  // drop through
            when (size) {
                3 -> {
                    if (value3 == null) {
                        return true
                    }
                    if (value2 == null) {
                        return true
                    }
                    if (value1 == null) {
                        return true
                    }
                }

                2 -> {
                    if (value2 == null) {
                        return true
                    }
                    if (value1 == null) {
                        return true
                    }
                }

                1 -> if (value1 == null) {
                    return true
                }
            }
        } else {
            when (size) {
                3 -> {
                    if (value == value3) {
                        return true
                    }
                    if (value == value2) {
                        return true
                    }
                    if (value == value1) {
                        return true
                    }
                }

                2 -> {
                    if (value == value2) {
                        return true
                    }
                    if (value == value1) {
                        return true
                    }
                }

                1 -> if (value == value1) {
                    return true
                }
            }
        }
        return false
    }

    /**
     * Converts the flat map data to a map.
     */
    private fun convertToMap() {
        delegateMap = createDelegateMap()

        when (size) {
            3 -> {
                delegateMap!!.put(key3!!, value3!!)
                delegateMap!!.put(key2!!, value2!!)
                delegateMap!!.put(key1!!, value1!!)
            }

            2 -> {
                delegateMap!!.put(key2!!, value2!!)
                delegateMap!!.put(key1!!, value1!!)
            }

            1 -> delegateMap!!.put(key1!!, value1!!)
            0 -> {}
            else -> throw IllegalStateException("Invalid map index: $size")
        }

        size = 0
        hash3 = 0
        hash2 = hash3
        hash1 = hash2
        key3 = null
        key2 = key3
        key1 = key2
        value3 = null
        value2 = value3
        value1 = value2
    }

    /**
     * Create an instance of the map used for storage when in delegation mode.
     *
     *
     * This can be overridden by subclasses to provide a different map implementation.
     * Not every AbstractHashedMap is suitable, identity and reference based maps
     * would be poor choices.
     *
     *
     * @return a new AbstractHashedMap or subclass
     * @since 3.1
     */
    protected fun createDelegateMap(): MutableMap<K,V> = hashMapOf()

    /**
     * Gets the entrySet view of the map.
     * Changes made to the view affect this map.
     *
     *
     * NOTE: from 4.0, the returned Map Entry will be an independent object and will
     * not change anymore as the iterator progresses. To avoid this additional object
     * creation and simply iterate through the entries, use [.mapIterator].
     *
     *
     * @return the entrySet view
     */
    override val entries: MutableSet<MutableMapEntry<K, V>>
        get() {
            delegateMap?.let { return it.entries }
            return EntrySet(this)
        }

    /**
     * Compares this map with another.
     *
     * @param other  the object to compare to
     * @return true if equal
     */
    override fun equals(other: Any?): Boolean {
        if (other === this) {
            return true
        }
        delegateMap?.let { return it == other }
        if (other !is MutableMap<*, *>) {
            return false
        }
        val other = other as Map<*, *>
        if (size != other.size) {
            return false
        }
        if (isNotEmpty()) {
            var otherValue: Any? = null
            when (size) {
                3 -> {
                    if (!other.containsKey(key3)) {
                        return false
                    }
                    otherValue = other.get(key3)
                    if (!equals<Any?>(value3, otherValue)) {
                        return false
                    }
                    if (!other.containsKey(key2)) {
                        return false
                    }
                    otherValue = other.get(key2)
                    if (!equals<Any?>(value2, otherValue)) {
                        return false
                    }
                    if (!other.containsKey(key1)) {
                        return false
                    }
                    otherValue = other.get(key1)
                    if (!equals<Any?>(value1, otherValue)) {
                        return false
                    }
                }

                2 -> {
                    if (!other.containsKey(key2)) {
                        return false
                    }
                    otherValue = other.get(key2)
                    if (!equals<Any?>(value2, otherValue)) {
                        return false
                    }
                    if (!other.containsKey(key1)) {
                        return false
                    }
                    otherValue = other.get(key1)
                    if (!equals<Any?>(value1, otherValue)) {
                        return false
                    }
                }

                1 -> {
                    if (!other.containsKey(key1)) {
                        return false
                    }
                    otherValue = other.get(key1)
                    if (!equals<Any?>(value1, otherValue)) {
                        return false
                    }
                }
            }
        }
        return true
    }

    /**
     * Gets the value mapped to the key specified.
     *
     * @param key  the key
     * @return the mapped value, null if no match
     */
     override fun get(key: K): V? {
        delegateMap?.let { return it[key] }
        if (key == null) {
            when (size) {
                3 -> {
                    if (key3 == null) {
                        return value3
                    }
                    if (key2 == null) {
                        return value2
                    }
                    if (key1 == null) {
                        return value1
                    }
                }

                2 -> {
                    if (key2 == null) {
                        return value2
                    }
                    if (key1 == null) {
                        return value1
                    }
                }

                1 -> if (key1 == null) {
                    return value1
                }
            }
        } else if (isNotEmpty()) {
            val hashCode = key.hashCode()
            when (size) {
                3 -> {
                    if (hash3 == hashCode && key == key3) {
                        return value3
                    }
                    if (hash2 == hashCode && key == key2) {
                        return value2
                    }
                    if (hash1 == hashCode && key == key1) {
                        return value1
                    }
                }

                2 -> {
                    if (hash2 == hashCode && key == key2) {
                        return value2
                    }
                    if (hash1 == hashCode && key == key1) {
                        return value1
                    }
                }

                1 -> if (hash1 == hashCode && key == key1) {
                    return value1
                }
            }
        }
        return null
    }

    /**
     * Gets the standard Map hashCode.
     *
     * @return the hash code defined in the Map interface
     */
    override fun hashCode(): Int {
        if (delegateMap != null) {
            return delegateMap.hashCode()
        }
        var total = 0
        when (size) {
            3 -> {
                total += hash3 xor (value3?.hashCode() ?: 0)
                total += hash2 xor (value2?.hashCode() ?: 0)
                total += hash1 xor (value1?.hashCode() ?: 0)
            }

            2 -> {
                total += hash2 xor (value2?.hashCode() ?: 0)
                total += hash1 xor (value1?.hashCode() ?: 0)
            }

            1 -> total += hash1 xor (value1?.hashCode() ?: 0)
            0 -> {}
            else -> throw IllegalStateException("Invalid map index: $size")
        }
        return total
    }

    //val isEmpty: Boolean
        /**
         * Checks whether the map is currently empty.
         *
         * @return true if the map is currently size zero
         */
        //get() = size == 0

    /**
     * Gets the keySet view of the map.
     * Changes made to the view affect this map.
     * To simply iterate through the keys, use [.mapIterator].
     *
     * @return the keySet view
     */

    override val keys: MutableSet<K>
        get(){
            delegateMap?.let { return it.keys }
            return KeySet(this)
        }

    /**
     * Gets an iterator over the map.
     * Changes made to the iterator affect this map.
     *
     *
     * A MapIterator returns the keys in the map. It also provides convenient
     * methods to get the key and value, and set the value.
     * It avoids the need to create an entrySet/keySet/values object.
     * It also avoids creating the Map Entry object.
     *
     *
     * @return the map iterator
     */
//    fun mapIterator(): MapIterator<K,V> {
//        if (delegateMap != null) {
//            return delegateMap.mapIterator()
//        }
//        if (size == 0) {
//            return EmptyMapIterator.< K, V>emptyMapIterator<K,V>()
//        }
//        return Flat3Map.FlatMapIterator<K,V>(this)
//    }

    /**
     * Puts a key-value mapping into this map.
     *
     * @param key  the key to add
     * @param value  the value to add
     * @return the value previously mapped to this key, null if none
     */
     override fun put(key: K, value: V): V? {
        delegateMap?.let { return it.put(key, value) }
        // change existing mapping
        if (key == null) {
            when (size) {
                3 -> {
                    if (key3 == null) {
                        val old = value3
                        value3 = value
                        return old
                    }
                    if (key2 == null) {
                        val old = value2
                        value2 = value
                        return old
                    }
                    if (key1 == null) {
                        val old = value1
                        value1 = value
                        return old
                    }
                }

                2 -> {
                    if (key2 == null) {
                        val old = value2
                        value2 = value
                        return old
                    }
                    if (key1 == null) {
                        val old = value1
                        value1 = value
                        return old
                    }
                }

                1 -> if (key1 == null) {
                    val old = value1
                    value1 = value
                    return old
                }
            }
        } else if (isNotEmpty()) {
            val hashCode = key.hashCode()
            when (size) {
                3 -> {
                    if (hash3 == hashCode && key == key3) {
                        val old = value3
                        value3 = value
                        return old
                    }
                    if (hash2 == hashCode && key == key2) {
                        val old = value2
                        value2 = value
                        return old
                    }
                    if (hash1 == hashCode && key == key1) {
                        val old = value1
                        value1 = value
                        return old
                    }
                }

                2 -> {
                    if (hash2 == hashCode && key == key2) {
                        val old = value2
                        value2 = value
                        return old
                    }
                    if (hash1 == hashCode && key == key1) {
                        val old = value1
                        value1 = value
                        return old
                    }
                }

                1 -> if (hash1 == hashCode && key == key1) {
                    val old = value1
                    value1 = value
                    return old
                }
            }
        }

        // add new mapping
        when (size) {
            2 -> {
                hash3 = key?.hashCode() ?: 0
                key3 = key
                value3 = value
            }

            1 -> {
                hash2 = key?.hashCode() ?: 0
                key2 = key
                value2 = value
            }

            0 -> {
                hash1 = key?.hashCode() ?: 0
                key1 = key
                value1 = value
            }

            else -> {
                convertToMap()
                delegateMap!!.put(key, value)
                return null
            }
        }
        size++
        return null
    }

    /**
     * Puts all the values from the specified map into this map.
     *
     * @param from  the map to add
     * @throws NullPointerException if the map is null
     */
     override fun putAll(from: Map<out K, V>) {
        val size: Int = from.size
        if (size == 0) {
            return
        }
        if (delegateMap != null) {
            delegateMap!!.putAll(from)
            return
        }
        if (size < 4) {
            for (entry in from.entries) {
                put(entry.key, entry.value)
            }
        } else {
            convertToMap()
            delegateMap!!.putAll(from)
        }
    }

    /**
     * Deserializes the map in using a custom routine.
     *
     * @param in the input stream
     * @throws IOException if an error occurs while reading from the stream
     * @throws ClassNotFoundException if an object read from the stream cannot be loaded
     */
//    @Throws(IOException::class, java.lang.ClassNotFoundException::class)
//    private fun readObject(`in`: ObjectInputStream) {
//        `in`.defaultReadObject()
//        val count: Int = `in`.readInt()
//        if (count > 3) {
//            delegateMap = createDelegateMap()
//        }
//        for (i in count downTo 1) {
//            put(`in`.readObject() as K?, `in`.readObject() as V?)
//        }
//    }

    /**
     * Removes the specified mapping from this map.
     *
     * @param key  the mapping to remove
     * @return the value mapped to the removed key, null if key not in map
     */
     override fun remove(key: K): V? {
        delegateMap?.let { return it.remove(key) }
        if (isEmpty()) {
            return null
        }
        if (key == null) {
            when (size) {
                3 -> {
                    if (key3 == null) {
                        val old = value3
                        hash3 = 0
                        key3 = null
                        value3 = null
                        size = 2
                        return old
                    }
                    if (key2 == null) {
                        val old = value2
                        hash2 = hash3
                        key2 = key3
                        value2 = value3
                        hash3 = 0
                        key3 = null
                        value3 = null
                        size = 2
                        return old
                    }
                    if (key1 == null) {
                        val old = value1
                        hash1 = hash3
                        key1 = key3
                        value1 = value3
                        hash3 = 0
                        key3 = null
                        value3 = null
                        size = 2
                        return old
                    }
                    return null
                }

                2 -> {
                    if (key2 == null) {
                        val old = value2
                        hash2 = 0
                        key2 = null
                        value2 = null
                        size = 1
                        return old
                    }
                    if (key1 == null) {
                        val old = value1
                        hash1 = hash2
                        key1 = key2
                        value1 = value2
                        hash2 = 0
                        key2 = null
                        value2 = null
                        size = 1
                        return old
                    }
                    return null
                }

                1 -> if (key1 == null) {
                    val old = value1
                    hash1 = 0
                    key1 = null
                    value1 = null
                    size = 0
                    return old
                }
            }
        } else if (isNotEmpty()) {
            val hashCode = key.hashCode()
            when (size) {
                3 -> {
                    if (hash3 == hashCode && key == key3) {
                        val old = value3
                        hash3 = 0
                        key3 = null
                        value3 = null
                        size = 2
                        return old
                    }
                    if (hash2 == hashCode && key == key2) {
                        val old = value2
                        hash2 = hash3
                        key2 = key3
                        value2 = value3
                        hash3 = 0
                        key3 = null
                        value3 = null
                        size = 2
                        return old
                    }
                    if (hash1 == hashCode && key == key1) {
                        val old = value1
                        hash1 = hash3
                        key1 = key3
                        value1 = value3
                        hash3 = 0
                        key3 = null
                        value3 = null
                        size = 2
                        return old
                    }
                    return null
                }

                2 -> {
                    if (hash2 == hashCode && key == key2) {
                        val old = value2
                        hash2 = 0
                        key2 = null
                        value2 = null
                        size = 1
                        return old
                    }
                    if (hash1 == hashCode && key == key1) {
                        val old = value1
                        hash1 = hash2
                        key1 = key2
                        value1 = value2
                        hash2 = 0
                        key2 = null
                        value2 = null
                        size = 1
                        return old
                    }
                    return null
                }

                1 -> if (hash1 == hashCode && key == key1) {
                    val old = value1
                    hash1 = 0
                    key1 = null
                    value1 = null
                    size = 0
                    return old
                }
            }
        }
        return null
    }



    /**
     * Gets the map as a String.
     *
     * @return a string version of the map
     */
    override fun toString(): String {
        if (delegateMap != null) {
            return delegateMap.toString()
        }
        if (isEmpty()) {
            return "{}"
        }
        val buf= StringBuilder(128)
        buf.append('{')
        when (size) {
            3 -> {
                buf.append(if (key3 === this) "(this Map)" else key3)
                buf.append('=')
                buf.append(if (value3 === this) "(this Map)" else value3)
                buf.append(Char.COMMA)
                buf.append(if (key2 === this) "(this Map)" else key2)
                buf.append('=')
                buf.append(if (value2 === this) "(this Map)" else value2)
                buf.append(Char.COMMA)
                buf.append(if (key1 === this) "(this Map)" else key1)
                buf.append('=')
                buf.append(if (value1 === this) "(this Map)" else value1)
            }

            2 -> {
                buf.append(if (key2 === this) "(this Map)" else key2)
                buf.append('=')
                buf.append(if (value2 === this) "(this Map)" else value2)
                buf.append(Char.COMMA)
                buf.append(if (key1 === this) "(this Map)" else key1)
                buf.append('=')
                buf.append(if (value1 === this) "(this Map)" else value1)
            }

            1 -> {
                buf.append(if (key1 === this) "(this Map)" else key1)
                buf.append('=')
                buf.append(if (value1 === this) "(this Map)" else value1)
            }

            else -> throw IllegalStateException("Invalid map index: $size")
        }
        buf.append('}')
        return buf.toString()
    }

    /**
     * Gets the values view of the map.
     * Changes made to the view affect this map.
     * To simply iterate through the values, use [.mapIterator].
     *
     * @return the values view
     */

    override val values: MutableCollection<V>
        get()  {
            delegateMap?.let { return it.values }
            return Values(this)
        }
    
    /**
     * Serializes this object to an ObjectOutputStream.
     *
     * @param out the target ObjectOutputStream.
     * @throws IOException thrown when an I/O errors occur writing to the target stream.
     */
//    @Throws(IOException::class)
//    private fun writeObject(out: ObjectOutputStream) {
//        out.defaultWriteObject()
//        out.writeInt(size())
//        val it: MapIterator<*, *> = mapIterator()
//        while (it.hasNext()) {
//            out.writeObject(it.next()) // key
//            out.writeObject(it.getValue()) // value
//        }
//    }

    companion object{

        /** Exception message.  */
        const val NO_NEXT_ENTRY: String = "No next() entry in the iteration"

        /** Exception message.  */
        const val REMOVE_INVALID: String = "remove() can only be called once after next()"

        /** Exception message.  */
        const val GETKEY_INVALID: String = "getKey() can only be called after next() and before remove()"

        /** Exception message.  */
        const val GETVALUE_INVALID: String = "getValue() can only be called after next() and before remove()"

        /** Exception message.  */
        const val SETVALUE_INVALID: String = "setValue() can only be called after next() and before remove()"

    }
    
}
